/*
 * parser.cpp
 *
 *  Created on: Feb 3, 2011
 *      Author: akcilap
 */



#include "parser.h"
#include "stack.h"
#include <cstdio>
#include <iostream>
#include <cstdlib>
int* matchCycles(const std::string& code);
void parseCode(const std::string& code, int size) {
#ifdef DEBUG
	std::cout << "parsing\n";
#endif
	//hlavne pole s datami, zo zaciatku je kazda bunka inicializovana na 0
	int* main_array = new int[size];
	for(int i = 0; i < size; i++) {
		main_array[i] = 0;
	}
	//index bunky s ktorou prave pracujeme
	int current_cell = 0;
	/*
	 * pole, ktore pre kazdu instrukciu obsahuje informaciu kam skocit v pripade cyklu
	 * '0' je pre normalnu instrukciu, v pripade '[' je tam cislo vacsie ako i, v pripade ']' opacne
	 */
	int* cycles = matchCycles(code);
/*#ifdef DEBUG
	std::cout << "no. of ins: " << code.size() << '\n';
#endif*/

	//index jednotlivyh instrukcii
	unsigned int i = 0;
	while(i < code.size()) {
#/*ifdef DEBUG
		std::cout << "ins: " << code[i] << "; cell_pos: " << current_cell
				<< "; cell_val int: " << (int)main_array[current_cell] << "; cell_val char: " << main_array[current_cell]
				<< "codePos: " << i << '\n';
#endif*/
		//tu prevadzame jednotlive instrukcie
		switch(code[i]) {
		case '+':
			main_array[current_cell]++;
			break;
		case '-':
			main_array[current_cell]--;
			break;
		case '<':
			if(current_cell == 0) {
				std::cerr << "Out of range\n";
				exit(1);
			} else {
				current_cell--;
			}
			break;
		case'>':
			if(current_cell == size-1) {
				std::cerr << "Out of range\n";
				exit(1);
			} else {
				current_cell++;
			}
			break;
		case '.':
			putchar(main_array[current_cell]);
			break;
		case ',':
			main_array[current_cell] = getchar();
			break;
		case '[':
			if(main_array[current_cell] == 0) {
				i = cycles[i];
			}
			break;
		case ']':
			if(main_array[current_cell] > 0) {
				i = cycles[i];
			}
			break;
		}
		i++;
	}
	delete main_array;
}

void convertToC(const std::string& code, std::ofstream& file, int size) {
	file << "#include <stdio.h>\n"
			"int main() {\n"
			"\tint main_array[" << size << "];\n"
			"\tint current_cell = 0;\n"
			"\tint i;\n"
			"\tfor(i = 0; i < " << size << "; i++) {\n"
			"\tmain_array[i] = 0;\n"
			"}\n";
	unsigned int i = 0;
	int indent = 1;
	std::cout << code.size() << std::endl;
	while(i < code.size()) {
		for(int j = 0; j < indent; j++) {
			file << '\t';
		}
		std::cout << i << ": " << code[i] << std::endl;
		int charCount = 0;
		unsigned int nextIns = i;
		/*
		 * mierne zjednodusenie vysledneho kodu - v pripade, ze nasleduje viac rovnakych instrukcii, ktore posuvaju index pola, pripadne menia hodnotu
		 * premennej, zlucime ich do jednej a danu hodnotu pricitame. V tomto cykle pocitame kolko instrukcii za sebou nasleduje
		 */
		if((code[i] == '+' || code[i] == '-' || code[i] == '>' || code[i] == '<')) {
			while(code[nextIns] == code[i] ) {
				charCount++;
				nextIns++;
			}
		} else {
			nextIns++;
		}
		switch(code[i]) {
		case '[':
			file << "while(main_array[current_cell] > 0) {\n";
			indent++;
			break;
		case ']':
			file << "\b}\n";
			indent--;
			break;
		case '+':
			if(charCount == 1) {
				file << "main_array[current_cell]++";
			} else {
				file << "main_array[current_cell] = main_array[current_cell] + " << charCount;
			}
			file << ";\n";
			break;
		case '-':
			if(charCount == 1) {
				file << "main_array[current_cell]--";
			} else {
				file << "main_array[current_cell] = main_array[current_cell] - " << charCount;
			}
			file << ";\n";
			break;
		case '<':
			if(charCount == 1) {
				file << "current_cell--";
			} else {
				file << "current_cell = current_cell - " << charCount;
			}
			file << ";\n";
			break;
		case '>':
			if(charCount == 1) {
				file << "current_cell++";
			} else {
				file << "current_cell = current_cell + " << charCount;
			}
			file << ";\n";
			break;
		case '.':
			file << "putchar(main_array[current_cell]);\n";
			break;
		case ',':
			file << "main_array[current_cell] = getchar();\n";
			break;
		}
		i = nextIns;
	}
	file << "\treturn 0;\n}";
	file.close();
}


int* matchCycles(const std::string& code) {
	int* cPos = new int[code.size()];
	for(unsigned int i = 0; i < code.size(); i++) {
		cPos[i] = 0;
	}
	Stack cBegs;
	for(unsigned int i = 0; i < code.size(); i++) {
		if(code[i] == '[') {
			cBegs.push(i);
		} else if(code[i] == ']') {
			if(cBegs.getSize() == 0) {
				std::cerr << "Unmatched cycles\n";
			} else {
				int beg = cBegs.getTop();
				cBegs.pop();
				cPos[beg] = i;
				cPos[i] = beg;
			}
		}
	}
	return cPos;
}
